using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LS.UnityExtensions;

namespace LS.Digitize
{
    public static class SerializerUtil
    {
        private static object s_LockObj = new object();

        public static Member[] GetMembers(Type type, string[] memberNames = null)
        {
            var refMembers = LSReflection.GetSerializableMembers(type, memberNames);
            Member[] members = refMembers.Select(rm=> new Member(rm)).ToArray();
            return members;
        }
        
        public static ISerializer GetOrCreateSerializer(Type type)
        {
            if (SerializerManager.Count == 0)
            {
                InitSerializerManager();
            }
            
            ISerializer serializer = SerializerManager.GetSerializer(type);
            if (serializer != null)
            {
                return serializer;
            }

            serializer = Create(type);
            if (serializer != null)
            {
                SerializerManager.Add(type, serializer);
                return serializer;
            }
            
            return null;
        }
   
        private static ISerializer Create(Type type)
        {
            ISerializer serializer = null;
            if (LSReflection.IsEnum(type))
            {
                serializer = new EnumSerializer(type);
            }
            if (LSReflection.IsPrimitive(type))
            {
                serializer = CreatePrimitive(type);
            }
            else if (LSReflection.TypeIsArray(type))
            {
                int rank = LSReflection.GetArrayRank(type);
                if(rank == 1)
                    serializer = CreateArray(type);
                // else if(rank == 2)
                //     serializer = new ArraySerializer(type);
                // else if(rank == 3)
                //     serializer = new ArraySerializer(type);
                else
                    return null;
            }
            else if(LSReflection.IsGenericType(type) && LSReflection.ImplementsInterface(type, typeof(IEnumerable)))
			{
				Type genericType = LSReflection.GetGenericTypeDefinition(type);
                if (typeof(List<>).IsAssignableFrom(genericType))
                {
                    serializer = CreateList(type);
                }
                else if (typeof(Dictionary<,>).IsAssignableFrom(genericType))
                {
                    serializer = CreateDictionary(type);
                }
                // else if (genericType == typeof(Queue<>))
                //     serializer = new QueueSerializer(type);
                // else if (genericType == typeof(Stack<>))
                //     serializer = new StackSerializer(type);
                // else if (genericType == typeof(HashSet<>))
                //     serializer = new HashSetSerializer(type);
                // else if (genericType == typeof(Unity.Collections.NativeArray<>))
                //     serializer = new NativeArraySerializer(type);
                // Else see if there is an Serializer with the generic type definition.
                else if((serializer = SerializerManager.GetSerializer(genericType)) != null)
                {
                    
                }
                else return null;
			}
			else
			{
   //              if (LSReflection.IsAssignableFrom(typeof(Component), type))
   //                  serializer = new ReflectedComponentSerializer(type);
   //              else if (ES3Reflection.IsValueType(type))
   //                  serializer = new ReflectedValueSerializer(type);
   //              else if (ES3Reflection.IsAssignableFrom(typeof(ScriptableObject), type))
   //                  serializer = new ReflectedScriptableObjectSerializer(type);
   //              else if (ES3Reflection.IsAssignableFrom(typeof(UnityEngine.Object), type))
   //                  serializer = new ReflectedUnityObjectSerializer(type);
   //              else if (type.Name.StartsWith("Tuple`"))
   //                  serializer = new TupleSerializer(type);
   //              else
                serializer = new ReflectedSerializer(type);
            }
			if(serializer == null || serializer.DataType == null || serializer.IsUnsupported)
			{
				return null;
			}
            return serializer;
        }

        private static ISerializer CreatePrimitive(Type type)
        {
            ISerializer serializer = null;
            if (type == typeof(string))
            {
                serializer =  new StringSerializer();
            }
            else if (type ==  typeof(char))
            {
                serializer =  new CharSerializer();
            }
            else if (type ==  typeof(bool))
            {
                serializer =  new BoolSerializer();
            }
            else if (type == typeof(byte))
            {
                serializer =  new ByteSerializer();
            }
            else if (type == typeof(decimal))
            {
                serializer =  new DecimalSerializer();
            }
            else if (type == typeof(double))
            {
                serializer =  new DoubleSerializer();
            }
            else if (type == typeof(float))
            {
                serializer =  new FloatSerializer();
            }
            else if (type == typeof(int))
            {
                serializer =  new IntSerializer();
            }
            else if (type == typeof(uint))
            {
                serializer =  new UIntSerializer();
            }
            else if (type == typeof(long))
            {
                serializer =  new LongSerializer();
            }
            else if (type == typeof(ulong))
            {
                serializer =  new ULongSerializer();
            }
            else if (type == typeof(short))
            {
                serializer =  new ShortSerializer();
            }
            else if (type == typeof(ushort))
            {
                serializer =  new UShortSerializer();
            }
            else if (type == typeof(sbyte))
            {
                serializer =  new SByteSerializer();
            }
            return serializer;
        }

        private static ISerializer CreateArray(Type type)
        {
            ISerializer serializer = null;
            if (type == typeof(string[]))
            {
                serializer =  new StringArraySerializer();
            }
            else if (type ==  typeof(char[]))
            {
                serializer =  new CharArraySerializer();
            }
            else if (type ==  typeof(bool[]))
            {
                serializer =  new BoolArraySerializer();
            }
            else if (type == typeof(byte[]))
            {
                serializer =  new ByteArraySerializer();
            }
            else if (type == typeof(decimal[]))
            {
                serializer =  new DecimalArraySerializer();
            }
            else if (type == typeof(double[]))
            {
                serializer =  new DoubleArraySerializer();
            }
            else if (type == typeof(float[]))
            {
                serializer =  new FloatArraySerializer();
            }
            else if (type == typeof(int[]))
            {
                serializer =  new IntArraySerializer();
            }
            else if (type == typeof(uint[]))
            {
                serializer =  new UIntArraySerializer();
            }
            else if (type == typeof(long[]))
            {
                serializer =  new LongArraySerializer();
            }
            else if (type == typeof(ulong[]))
            {
                serializer =  new ULongArraySerializer();
            }
            else if (type == typeof(short[]))
            {
                serializer =  new ShortArraySerializer();
            }
            else if (type == typeof(ushort[]))
            {
                serializer =  new UShortArraySerializer();
            }
            else if (type == typeof(sbyte[]))
            {
                serializer =  new SByteArraySerializer();
            }
            else
            {
                var elementTypes = LSReflection.GetElementTypes(type);
                serializer = new ArraySerializer(type,elementTypes[0]);
            }
            return serializer;
        }
        
        private static ISerializer CreateList(Type type)
        {
            ISerializer serializer = null;
            if (type == typeof(List<string>))
            {
                serializer =  new StringArraySerializer();
            }
            else if (type ==  typeof(List<char>))
            {
                serializer =  new CharSerializer();
            }
            else if (type ==  typeof(List<bool>))
            {
                serializer =  new BoolSerializer();
            }
            else if (type == typeof(List<byte>))
            {
                serializer =  new ByteSerializer();
            }
            else if (type == typeof(List<decimal>))
            {
                serializer =  new DecimalSerializer();
            }
            else if (type == typeof(List<double>))
            {
                serializer =  new DoubleSerializer();
            }
            else if (type == typeof(List<float>))
            {
                serializer =  new FloatSerializer();
            }
            else if (type == typeof(List<int>))
            {
                serializer =  new IntSerializer();
            }
            else if (type == typeof(List<uint>))
            {
                serializer =  new UIntSerializer();
            }
            else if (type == typeof(List<long>))
            {
                serializer =  new LongSerializer();
            }
            else if (type == typeof(List<ulong>))
            {
                serializer =  new ULongSerializer();
            }
            else if (type == typeof(List<short>))
            {
                serializer =  new ShortSerializer();
            }
            else if (type == typeof(List<ushort>))
            {
                serializer =  new UShortSerializer();
            }
            else if (type == typeof(List<sbyte>))
            {
                serializer =  new SByteSerializer();
            }
            else
            {
                var elementTypes = LSReflection.GetElementTypes(type);
                serializer = new ListSerializer(type,elementTypes[0]);
            }
            return serializer;
        }

        private static ISerializer CreateDictionary(Type type)
        {
            Type[] elementTypes = LSReflection.GetElementTypes(type);
            ISerializer serializer = new DictionarySerializer(type, elementTypes[0], elementTypes[1]);
            return serializer;
        }
        
        private static void InitSerializerManager()
        {
            lock (s_LockObj)
            {
                var serializerType = typeof(ISerializer);
                var primitiveSerializer = typeof(IPrimitiveSerializer);
                
                var serializerSearch = LSReflection.GetTypes()
                    .Where(t=> !t.IsAbstract && !t.IsInterface && LSReflection.IsAssignableFrom(serializerType, t));

                IEnumerable<Type> primitiveSearch = serializerSearch
                    .Where(t => !t.IsInterface && !t.IsAbstract && !t.IsGenericTypeDefinition && LSReflection.IsAssignableFrom(primitiveSerializer, t) &&
                                LSReflection.HasParameterlessConstructor(t));
                
                foreach (var primitiveType in primitiveSearch)
                {
                    Serializer ps = LSReflection.CreateInstance(primitiveType) as Serializer;
                    SerializerManager.Add(ps.DataType, ps);
                }
                
                IEnumerable<Type> compoundSerializers = serializerSearch
                    .Where(t=> !LSReflection.IsAssignableFrom(primitiveSerializer, t) && !t.IsGenericTypeDefinition && LSReflection.HasParameterlessConstructor(t));
                
                foreach (Type compoundSerializer in compoundSerializers)
                {
                    ISerializer ps = LSReflection.CreateInstance(compoundSerializer) as ISerializer;
                    SerializerManager.Add(ps.DataType, ps);
                }
                
            }
        }
    }
}